﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OracleClient;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading;

namespace NotesCommonProjects
{
    public class MSGKit
    {
        private const string kMSGKind = "iSender";
        private static System.Text.Encoding kMSGEncoding = System.Text.Encoding.GetEncoding("big5");
        private const int kConnTimeoutDefault = 5000;//ms
        private const string kIPAddrPattern = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";

        private static string GetConnStrErp()
        {
            return "SERVER=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=203.69.132.121)(PORT=1521)))(CONNECT_DATA =(SERVER=DEDICATED)(SERVICE_NAME = erp)));uid=AC;pwd=T5001855";
        }


        /// <summary>傳送 Windows CCP MSG 訊息(台北總公司) (new Thread)</summary>
        /// <param name="phoneNo">總公司分機號碼</param>
        /// <param name="message">訊息全文</param>
        public static void SendTextHQAsync(string phoneNo, string message)
        {
            CCPMsgParam paramObj = new CCPMsgParam(phoneNo, message);
            new Thread(new ParameterizedThreadStart(SendTextHQ)).Start(paramObj);
        }


        /// <summary>執行 Windows CCP MSG 訊息傳送(台北總公司)</summary>
        /// <param name="param">CCPMsgParam參數物件</param>
        private static void SendTextHQ(object param)
        {
            if (!(param is CCPMsgParam))
            {
                return;
            }

            OracleConnection connMIS = new OracleConnection(GetConnStrErp());

            try
            {
                CCPMsgParam p = (CCPMsgParam)param;
                string phoneNo = p.PhoneNo;

                if (Regex.IsMatch(phoneNo, "^62[0-9]{3}$"))
                {
                    phoneNo = phoneNo.Substring(2, 3);
                }
                else if (isHQMobileExt(phoneNo))
                {
                    phoneNo = getHQDesktopExt(phoneNo);
                }

                connMIS.Open();

                //【01】找出目標電腦的 IP 與 MSG Port
                string selStr = "SELECT IP, PORT FROM IPPHONE_LOG"
                  + " WHERE PHONE_AREA='62' AND PHONE_NO=:PHONE_NO"
                  + " AND LOG_TIME>=TRUNC(SYSDATE-180)";
                OracleCommand selCmd = new OracleCommand(selStr, connMIS);
                selCmd.Parameters.Add("PHONE_NO", OracleType.VarChar).Value = phoneNo;
                OracleDataAdapter adapter = new OracleDataAdapter(selCmd);
                DataSet dataSet = new DataSet();
                adapter.Fill(dataSet);

                if (dataSet.Tables[0].Rows.Count > 0)
                {
                    DataRow row = dataSet.Tables[0].Rows[0];
                    string msgIP = Convert.ToString(row["IP"]);
                    int msgPort = Convert.ToInt32(row["PORT"]);

                    if (Regex.IsMatch(msgIP, kIPAddrPattern))
                    {
                        //【02】Socket 連接目標電腦的 MSG
                        TcpClientWithTimeout tClient = new TcpClientWithTimeout(msgIP, msgPort, kConnTimeoutDefault);
                        TcpClient clientSocket = tClient.Connect();
                        NetworkStream serverStream = clientSocket.GetStream();
                        serverStream.WriteTimeout = 2000;
                        serverStream.ReadTimeout = 2000;


                        //【03】調整跳脫字元
                        /*
                        string tweakMsg = p.Message;
                        tweakMsg = tweakMsg.Replace(",", "，");    //逗號會被當成換行，用全形替換
                        tweakMsg = tweakMsg.Replace(" ", "　");    //空格不能傳，用全形替換
                        tweakMsg = tweakMsg.Replace("&", "＆");    //"&"不能傳，用全形替換
                        tweakMsg = tweakMsg.Replace("]: ", "]:\n");//文字訊息開頭當做第一行
                        tweakMsg = tweakMsg.Replace("\n", ",");    //換行要用逗號表示
                        */

                        //【04】依格式送出訊息
                        //string fullParams = string.Format("Kind={0}&Hint={1}", kMSGKind, tweakMsg);
                        //string fullParams = "Kind=iSender&Hint=" + p.Message;
                        string fullParams = p.Message;
                        byte[] cmdBytes = kMSGEncoding.GetBytes(fullParams);
                        serverStream.Write(cmdBytes, 0, cmdBytes.Length);

                        //【99】關閉 Socket 連線
                        serverStream.Close();
                        clientSocket.Close();
                        serverStream = null;
                        clientSocket = null;
                    }
                }
            }
            catch (Exception ex)
            {
                SkipVar(ex);
            }
            finally
            {
                connMIS.Close();
            }
        }

        public static void SkipVar(object obj)
        {
            // Use this function to skip unused variable
        }

        /// <summary>判斷是否為總公司行動分機</summary>
        /// <param name="SIP_Num">SIP分機號碼</param>
        /// <returns>總公司行動分機回傳true，否則false</returns>
        public static bool isHQMobileExt(string SIP_Num)
        {
            if (Regex.IsMatch(SIP_Num, "^88[1-9][0-9]{2}[1-9]?$"))
                return true;
            else
                return false;
        }

        /// <summary>由總公司行動分機轉換桌機號碼</summary>
        /// <param name="mobileExt">總公司行動分機號碼</param>
        /// <returns>總公司桌機號碼或null</returns>
        public static string getHQDesktopExt(string mobileExt)
        {
            if (isHQMobileExt(mobileExt))
                return mobileExt.Substring("88".Length, 3);
            else
                return null;
        }

        /// <summary>Windows CCP MSG 呼叫參數</summary>
        private class CCPMsgParam
        {
            private string _PhoneNo;
            private string _Message;

            public string PhoneNo { get { return this._PhoneNo; } }
            public string Message { get { return this._Message; } }


            public CCPMsgParam(string phoneNo, string message)
            {
                _PhoneNo = phoneNo;
                _Message = message;
            }
        }

    }

    /// <summary>
    /// TcpClientWithTimeout is used to open a TcpClient connection, with a
    /// user definable connection timeout in milliseconds (1000=1second)
    /// Use it like this:
    /// TcpClient connection = new TcpClientWithTimeout('127.0.0.1',80,1000).Connect();
    /// </summary>
    public class TcpClientWithTimeout
    {
        protected string _hostname;
        protected int _port;
        protected int _timeout_milliseconds;
        protected TcpClient connection;
        protected bool connected;
        protected Exception exception;

        public TcpClientWithTimeout(string hostname, int port, int timeout_milliseconds)
        {
            _hostname = hostname;
            _port = port;
            _timeout_milliseconds = timeout_milliseconds;
        }

        public TcpClient Connect()
        {
            // kick off the thread that tries to connect
            connected = false;
            exception = null;
            Thread thread = new Thread(new ThreadStart(BeginConnect));
            thread.IsBackground = true; // So that a failed connection attempt
            // won't prevent the process from terminating while it does the long timeout
            thread.Start();

            // wait for either the timeout or the thread to finish
            thread.Join(_timeout_milliseconds);

            if (connected == true)
            {
                // it succeeded, so return the connection
                thread.Abort();
                return connection;
            }
            if (exception != null)
            {
                // it crashed, so return the exception to the caller
                thread.Abort();
                throw exception;
            }
            else
            {
                // if it gets here, it timed out, so abort the thread and throw an exception
                thread.Abort();
                string message = string.Format("TcpClient connection to {0}:{1} timed out",
                  _hostname, _port);
                throw new TimeoutException(message);
            }
        }

        protected void BeginConnect()
        {
            try
            {
                connection = new TcpClient(_hostname, _port);
                // record that it succeeded, for the main thread to return to the caller
                connected = true;
            }
            catch (Exception ex)
            {
                // record the exception for the main thread to re-throw back to the calling code
                exception = ex;
            }
        }
    }
}